//General
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;


//Project
using CS6238Project2;
using CS6238Project2.Banking.Commerce;
using CS6238Project2.Banking.Communication;
using CS6238Project2.Banking.Tools;
using CS6238Project2.Banking.Entity;

//Serialization
using System.IO;
using System.Xml;
using System.Xml.Serialization;

using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;

namespace CS6238Project2.GUI
{
    [Serializable]
    public partial class MerchantDisplay : Form
    {
        /*
        * TODO:
        *  Network:
        *      Handling of receiver
        *      Handling of Transmitter
        *      Handling of Network Config changing receiver/transmitter
        *            -Use the networkWindow_ButtonClicked method.
        *            -e contains all necessary data.
        * 
          * Must handle parcel sending for all these functons:
         * 
                //resyncToolStripMenuItem_Click
                
         * 
         * 
	            //bankOpenAccountButton_Click
         * 
         * 
	            //bankTransactionWithdrawButton_Click
	            //bankTransactionDepositButton_Click
         * 
         *      //inventoryOrderButton_Click
	            //merchantOpenAccountButton_Click
        * 
        */

        NetworkControlForm networkWindow;//network controls
        InventoryManagerWindow inventoryManager;
        Merchant merchant;
        public Server server;
        Parcel globalParcel;
        int currentMerchantsInventoryRow;


        public Merchant Merchant
        {
            get { return merchant; }
            set { merchant = value; }
        }



        public MerchantDisplay()
        {

            //Networking
            Random random = new Random();
            server = new Server(random.Next(10000, 20000),false);
            server.ParcelPassed += new Server.ParcelDelegate(Parcel_Received);


            //The Merchant
            merchant = new Merchant();
            merchant.Account = new Account();
            



            WordList wordlist = new WordList();
            
            //merchant.goods = Spawn.Purchases(wordlist, 10);
            inventoryManager = new InventoryManagerWindow();
            

            InitializeComponent();
            networkWindow = new NetworkControlForm();//set up network control


            //Data Bindings
            inventoryManager.GoodsBindingSource.DataSource = merchant.Inventory;
            goodsBindingSource.DataSource = merchant.Goods;//Bind the inventory
            merchantBindingSource.DataSource = merchant.Merchants;//Bind the merchant list
            purchasesBindingSource.DataSource = merchant.Purchases;//bind their list of purchses
            recordBindingSource.DataSource = merchant.Account.Record;//Bind the account.


            merchant.Inventory = Spawn.Goods(wordlist, 15);
            inventoryManager.GoodsBindingSource.DataSource = merchant.Inventory;
            merchant.Purchases = Spawn.Purchases(wordlist, 10);
            purchasesBindingSource.DataSource = merchant.Purchases;
           
        }


        /************************************************************************************/
        /***                        GLOBAL EVENT HANDLING                                 ***/
        /************************************************************************************/


        Thread dataThread;

        private void Parcel_Received(Parcel parcel)
        {
            Console.WriteLine(
                "Sender: " + parcel.Sender +
                "\nReceiver: " + parcel.Receiver +
                "\nType: " + parcel.Type +
                "\nMessageData: " + parcel.MessageData + "\n"
            );
            globalParcel = parcel;
            dataThread = new Thread(new ThreadStart(this.ThreadProcSafe));
            this.dataThread.Start();
        }


        /// <summary>
        /// Updates the binding source for the selected merchant's inventory with the global data
        /// </summary>
        private void UpdateMerchantsInventory()
        {
            if (globalParcel.InventoryList != null)
            {
                merchant.Goods = globalParcel.InventoryList;
                merchantsInventoryGridView.DataSource = merchant.Goods;
            }
        }
        /// <summary>
        /// Updates the binding source for the list of merchants with the global data
        /// </summary>
        private void UpdateMerchantsList()
        {
            if (globalParcel.NameList != null)
            {
                merchant.Merchants = globalParcel.NameList;
                merchantListBox.DataSource = merchant.Merchants;
            }
        }
        /// <summary>
        /// Updates the binding source for the transaction record with the global data
        /// </summary>
        private void UpdateTransactionsRecord()
        {
            if (globalParcel.Account != null)
            {
                merchant.Account = globalParcel.Account;
                recordGridView.DataSource = merchant.Account.Record;
            }
        }

        // This method is executed on the worker thread and makes
        // a thread-safe call on the MainApp control.
        private void ThreadProcSafe()
        {
            //Bank replying to registration, we need to set the recordgridview to edit
            if ((globalParcel.SenderPort == 8000) &&
                (globalParcel.Type == "Registration Response"))
            {
                if (recordGridView.InvokeRequired)
                {
                    recordGridView.Invoke(new MethodInvoker(ProcessParcel));
                }
                else
                {
                    ProcessParcel();
                }
            }
            //It's a list response, so we're going to set
            //The Record, Merchant, Merchant inventor grid views to update
            else if (globalParcel.Type == "List Response")
            {
                //Inventory of selected merchant
                if (merchantsInventoryGridView.InvokeRequired)
                {
                    merchantsInventoryGridView.Invoke(new MethodInvoker(UpdateMerchantsInventory));
                }
                else
                {
                    UpdateMerchantsInventory();
                }

                //List of merchants
                if (merchantListBox.InvokeRequired)
                {
                    merchantListBox.Invoke(new MethodInvoker(UpdateMerchantsList));
                }
                else
                {
                    UpdateMerchantsList();
                }

                //Transaction Record
                if (recordGridView.InvokeRequired)
                {
                    recordGridView.Invoke(new MethodInvoker(UpdateTransactionsRecord));
                }
                else
                {
                    UpdateTransactionsRecord();
                }

            }
            else if (globalParcel.Type == "Order response")
            {
                if (purchasesGridView.InvokeRequired)
                {
                    purchasesGridView.Invoke(new MethodInvoker(ProcessParcel));
                }
                else
                {
                    ProcessParcel();
                }
            }
            else
            {
                ProcessParcel();
            }
            balanceTextBox.Invoke(new MethodInvoker(UpdateBalance));
        }


        /// <summary>
        /// This resyncs the transaction and Merchant lists
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void resyncToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                //Send a request to the bank for hte global list of Merchants and
                //the record for that account

                Parcel parcel = new Parcel(merchant.Name, "Bank of America", "Request");
                parcel.MessageData = "Lists";
                //SEND PARCEL TO BANK
                //INCOMPLETE
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }

        private void UpdateBalance()
        {
            balanceTextBox.Text = Convert.ToString(merchant.Account.Balance);
        }

        /// <summary>
        /// Opens the inventory manager window, which allows us to edit and add
        /// the inventory
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void inventoryWindowOpen_Click(object sender, EventArgs e)
        {
            inventoryManager.ShowDialog();
            //update our inventory!
        }


        /************************************************************************************/
        /***                                 MERCHANT HANDLING                            ***/
        /************************************************************************************/


        /// <summary>
        /// Add a merchant to the list
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void merchantOpenAccountButton_Click(object sender, EventArgs e)
        {
            try
            {
                string name = merchantListBox.SelectedItem as string;
                Parcel parcel = merchant.Register(name);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            //SEND PARCEL TO MERCHANT
            //INCOMPLETE
        }


        /// <summary>
        /// This handles the ordering of a selected item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void inventoryOrderButton_Click(object sender, EventArgs e)
        {
            try
            {
                int quantity = Convert.ToInt32(inventoryOrderTextBox.Text);
                int worth = (int) merchantsInventoryGridView[1, currentMerchantsInventoryRow].Value;
                string name = (string) merchantsInventoryGridView[0, currentMerchantsInventoryRow].Value;
                string merchantName = merchantListBox.SelectedItem as string;
                Parcel parcel = merchant.Order(merchantName, name, quantity, worth);
                parcel.SenderAddr = IP();
                server.Send(IP(), Port(), parcel);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            //SEND PARCEL TO MERCHANT
            //INCOMPLETE
        }



        /************************************************************************************/
        /***                    BANK AND ACCOUNT HANDLING                                 ***/
        /************************************************************************************/


        /// <summary>
        /// Opens a new account with the bank
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bankOpenAccountButton_Click(object sender, EventArgs e)
        {
            try
            {
                Parcel parcel = merchant.Register("Bank of America");
                parcel.MessageData = "Merchant";
                parcel.SenderAddr = IP();
                server.Send(IP(), Port(), parcel);
                //SEND PARCEL TO BANK
                //INCOMPLETE
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }

        /// <summary>
        /// Request a withdraw from the bank
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bankTransactionWithdrawButton_Click(object sender, EventArgs e)
        {
            try
            {
                int amount = -1 * Convert.ToInt32(transactionTextBox.Text);
                Parcel parcel = merchant.Transact(amount);
                parcel.SenderAddr = IP();
                server.Send(IP(), Port(), parcel);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            //SEND PARCEL TO BANK
            //INCOMPLETE
        }


        /// <summary>
        /// Request a deposit to the bank
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bankTransactionDepositButton_Click(object sender, EventArgs e)
        {
            try
            {
                int amount = Convert.ToInt32(transactionTextBox.Text);
                Parcel parcel = merchant.Transact(amount);
                parcel.SenderAddr = IP();
                server.Send(IP(), Port(), parcel);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            //SEND PARCEL TO BANK
            //INCOMPLETE
        }



       

        /************************************************************************************/
        /***                             PASSIVE FUNCTIONS                                ***/
        /************************************************************************************/

        
        //I am unsure how these should work, but something like this:

        /// <summary>
        /// Processes incoming information, after the receiver turns the incoming data
        /// into parcels to present to this function
        /// </summary>
        /// <param name="parcel">The incoming parcel</param>
        private void ProcessParcel()
        {

            Parcel parcel = new Parcel(globalParcel);
            bool gridRefresh = false;
            Parcel refreshParcel = null;
            try
            {

                ParcelError(parcel);//scan the parcel for errors to pop up
                //Check if we're just spooling in new lists
                

                //Check to see if we need to refresh the account information
                if ((parcel.Type == "Transaction Response") ||
                    (parcel.Type == "Dual Transaction Response") ||
                    (parcel.Type == "Registration Response"))
                {
                    gridRefresh = true;
                    if (parcel.MessageData == "Approved")
                    {
                        if (parcel.Type == "Registration Response") { MessageBox.Show("Registered successfully!"); }
                        if (parcel.Account == null)
                        {
                            refreshParcel = new Parcel(parcel);
                        }
                        else
                        {
                            //MEOW!!!! why is this here?
                        }
                    }
                    //SEND PARCEL refreshParcel to refreshParcel.Receiver
                }
                //Since we're not, it can be handled by the User object.
                parcel = merchant.ProcessParcel(parcel);

                if (parcel.Type != "Terminated")
                {
                    parcel.SenderAddr = IP();
                    server.Send(IP(), Port(), parcel);
                }

                if (gridRefresh)
                {
                    recordGridView.DataSource = merchant.Account.Record;
                }


                if (null != refreshParcel)
                {
                    refreshParcel.Receiver = refreshParcel.Sender;
                    refreshParcel.Sender = merchant.Name;
                    refreshParcel.Type = "List Request";
                    refreshParcel.Number = merchant.Account.Number;
                    parcel.SenderAddr = IP();
                    server.Send(IP(), Port(), refreshParcel);
                }             
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
        }




        /// <summary>
        /// Checks to see if a parcel contains a reject or some such, and if so
        /// will pop that error up.
        /// </summary>
        /// <param name="parcel"></param>
        private void ParcelError(Parcel parcel)
        {
            //This is an array that we can easily enter new error
            //messages into, and the error handling will otherwise
            //be unchanged
            string[] errors = 
                {
                    "Denied", 
                    "Account Not Found",
                    "Transaction data invalid",
                    "Exception Occurred",
                    "Transaction Not Approved",
                    "Invalid Parcel",
                    "User was not approved!"
                };

            //Cycle through the list, seeking anything appropriate.
            for (int i = 0; i < errors.Length; i++)
            {
                if (parcel.MessageData == errors[i])
                {
                    MessageBox.Show(errors[i]);
                }
            }


        }

        /************************************************************************************/
        /***                                NETWORK CONTROLS                              ***/
        /************************************************************************************/


        private void connectionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            /*
             * TODO:
             *  When first created, get port data from the transmitter/receiver
             *  When first created, determine connection status (virtual)
             * 
             */
            networkWindow = new NetworkControlForm();
            //networkWindow.listeningPort = 
            //networkWindow.sendingPort =
            networkWindow.Show();

        }



        private void testBox()
        {

            networkWindow.NetworkEvent +=
                new NetworkControlForm.
                NetworkControlHandler(networkWindow_ButtonClicked);
            networkWindow.Show();

        }

        private void networkWindow_ButtonClicked(object sender, NetworkControlEventArgs e)
        {
            try
            {
                bool DeterminesWhatConnectionButtonSays = e.Connected;
                int thisIsTheListeningPortValue = e.ListeningPort;
                int thisIsTheSendingPortValue = e.SendingPort;
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            //Update network settings

            //INCOMPLETE
        }

        /// <summary>
        /// This method autometically formats the coloring of the Transaction Record
        /// to display green when positive, black neutral, red when negative.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RecordTable_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {

            /*
             * TODO:
             *  NOTHING
             */
            try
            {
                if (e.ColumnIndex == 3)
                {
                    Transaction trans = merchant.Account.Record[e.RowIndex];
                    if (trans.Adjustment < 0)
                    {

                        recordGridView[e.ColumnIndex, e.RowIndex].Style.ForeColor = Color.Red;
                    }
                    else if (trans.Adjustment > 0)
                    {

                        recordGridView[e.ColumnIndex, e.RowIndex].Style.ForeColor = Color.Green;
                    }
                    else
                    {

                        recordGridView[e.ColumnIndex, e.RowIndex].Style.ForeColor = Color.Black;
                    }
                }
            }
            catch (ArgumentOutOfRangeException exception)
            {
                Console.WriteLine(exception);
            }
        }


        private int Port()
        {
            return 8000;
        }

        private string IP()
        {
            return "192.168.0.5";
        }


        /// <summary>
        /// Uses serialization to save the entire kernel as a file of itself.
        /// </summary>
        /// <param name="fileName">The name for the file to be saved as.</param>
        private void save()
        {

            Stream stream = null;
            try
            {
                IFormatter formatter = new BinaryFormatter();
                stream = new FileStream(merchant.Name + ".MerchantFile", FileMode.Create, FileAccess.Write, FileShare.None);
               


                String xmlString = Encryption.XmlString(merchant);
                formatter.Serialize(stream, xmlString);
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }
            finally
            {
                if (null != stream)
                    stream.Close();
            }
        }

        private void MerchantDisplay_FormClosing(object sender, FormClosingEventArgs e)
        {
            save();
        }


        /// <summary>
        /// Handle the selecting of different merchants and viewing their inventories
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void merchantListBox_SelectedIndexChanged(object sender, EventArgs e)
        {

            AddressEntry item = (AddressEntry)merchantListBox.SelectedItem;


            if (item.Name == merchant.Name)
            {
                merchantListBox.SelectedIndex = 0;
            }
            else
            {
                Parcel parcel = new Parcel();
                parcel.Sender = merchant.Name;
                parcel.Receiver = item.Name;
                parcel.Type = "List Request";
                parcel.SenderAddr = IP();
                parcel.Number = merchant.Account.Number;
                server.Send(IP(), 8000, parcel);
            }
        }

        

        private void purchasesGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            string name;

            try
            {
                if (e.ColumnIndex == 0)
                {
                    name = merchant.Purchases[e.RowIndex].Good.Name;
                    purchasesGridView[e.ColumnIndex, e.RowIndex].Value = name;
                }
            }

            catch (Exception exception)
            {
                Console.WriteLine(exception);
            }

        }
    }
}